Spring MVC

@VERO
Created Date · 2023년 04월 11일 10:04
Last Updated Date · 2023년 04월 11일 10:04

내용을 주관적으로 덜어냈습니다. 자세한 내용은 참고 링크를 확인해주세요 👍

Mapping

Request Mapping

Request Mapping은 Controller의 메서드에 Request를 매핑하기 위해 사용한다. class level에서 사용하면 shared mapping을 표현할 수 있다. method level에서 사용하면 특정 endpoint mapping 으로 좁혀서 표현할 수 있다.

HTTP 메서드 별로 @RequestMapping 의 변형은 다음과 같다.

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping
@RestController
@RequestMappping("/customers")
class CustomerController {

	@GetMapping("/{id}")
	public Customer getCustomer(@PathVariable Long id) {
		// ...
	}
}

@RequestMapping 어노테이션을 Controller 상단에 적어주면, Controller 내부의 모든 메서드가 같은 URL을 공유하게 된다.

@GetMapping 같은 specific annotation은 메서드 상단에 적는다. 해당 메서드가 어떤 URL에서 불려야할 지를 범위를 좁혀 특정지을 수 있다.

URI Patterns

@RequestMapping 은 URL 패턴을 이용해서 mapping 될 수 있다.

  • PathPattern : URL 경로와 매칭되는 사전 구문 분석된 패턴을 PathContainer 로 사전 구문 분석한다.
    • AntPathMatcher 와 달리 ** 가 패턴 끝에서만 지원된다. /pages/{**} 는 가능하지만, /pages/{**}/details 는 안 된다.
    • 참고: 공식문서
  • AntPathMatcher : 문자열 패턴을 문자열 경로와 일치시킨다. 덜 효율적이고, 문자열 경로 입력은 URL과 관련된 인코딩, 기타 문제를 효과적으로 처리하는데 어려움이 있다.

PathPattern 을 사용하는 것을 권장한다.

Media Types - produces

Request Header와 Controller Method가 생성하는 콘텐츠 유형 목록을 기반으로 Request Mapping의 범위를 좁힐 수 있다.

@GetMapping(path = "/pages/{id}", produces = "application/json")

위 예제에서 /pages/{id} 로 들어오는 요청 중 "application/json" 속성을 갖는 요청만 해당 메서드로 mapping 된다.

!text/plain 같은 부정 표현도 지원된다.

class level에서 produces 을 사용하면 class level에서 produces 가 공유된다. 그러나 method level에서 produces 를 사용하면 class level에서 설정이 확장되는 것이 아니라 method level의 produces 로 설정이 덮어 씌워진다.

MediaType 중에 빈번하게 사용되는 media type을 상수로 제공한다. Ex. APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE

Media Types - consumes

Request Header와 Controller Method가 생성하는 콘텐츠 유형 목록을 기반으로 Request Mapping의 범위를 좁힐 수 있다.

@PostMapping(path = "/page", consumes = "application/json")

위 예제에서 /pages/{id} 로 들어오는 요청 중 "application/json" 속성을 갖는 요청만 해당 메서드로 mapping 된다.

!text/plain 같은 부정 표현도 지원된다.

class level에서 consumes 을 사용하면 class level에서 consumes 가 공유된다. 그러나 method level에서 consumes 를 사용하면 class level에서 설정이 확장되는 것이 아니라 method level의 consumes 로 설정이 덮어 씌워진다.

MediaType 중에 빈번하게 사용되는 media type을 상수로 제공한다. Ex. APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE

Parameters, headers

Request Parameter 조건에 따라 Request Mapping의 범위를 좁힐 수 있다. Request Parameter가 존재하는지, 아닌지, 특정 값을 갖는지 테스트할 수 있다.

@GetMapping(path = "/pages/{pageId}", params = "myParam=myValue")

myValue 값을 갖는 myParam 인 경우에만 해당 메서드가 실행된다.

Content-Type, Accept를 체크할 수도 있지만, consumes나 produces를 사용하는 것이 더 낫다.

Handler

Method Arguments

종류가 너무 많으므로, 학습 테스트에서 다룬 @RequestBody , @RequestParam 만 살펴보겠다.

@RequestParam

@RequestParam 은 Multipart file을 포함해서 Request Parameter에 접근할 수 있게 한다. Parameter 값은 선언된 메서드 매개변수로 바인딩 된다.

기본적으로는 @RequestParam 을 사용하는 메서드의 매개변수가 필수이지만, requiredfalse 로 선언하면 메서드 매개변수가 선택 사항이라는 것을 지정할 수 있다.

@GetMapping
public String setupForm(@RequestParam("petId") int petId, Model model) {
	// ...
}

Parameter 타입이 String이 아닌 경우에는 타입 변환이 자동으로 일어난다.

@RequestBody

@PostMapping("/accounts") 
public void handle(@RequestBody Account account) { // ... }

@Valid annotation과 같이 조합되어 사용될 수 있다.

Return Values

@ResponseBody

자바 객체를 HTTP Request body로 전송할 수 있다. class level을 지원한다. class level에서 사용하면 모든 controller method에서 사용할 수 있다.

@GetMapping("/account/{id}")
@ResponseBody
public Actor handle() {
	// ...
}

@ResponseBody 어노테이션이 적용된 메서드는 HttpMessageConverter 를 사용해서 변환을 처리한다.

ResponseEntity<B>

@ResponseBody 와 비슷하지만 HttpStatus와 HttpHeaders, HttpBody가 있다.

@GetMapping("/something")
public ResponseEntity<String> handle() {
	String body = ... ;
	String etag = ... ;
	return ResponseEntity.ok().eTag(etag).body(body);
}

ResponseEntity 가 제네릭 타입

Exception

@ExceptionHandler 로 Controller 메서드의 예외를 처리하는 메서드를 가질 수 있다.

@ExceptionHandler 
public ResponseEntity<String> handle(IOException ex) { // ... }

일치시킬 예외 유형을 좁힐 수도 있다.

@ExceptionHandler({FileSystemException.class, RemoteException.class}) public ResponseEntity<String> handle(IOException ex) { 
	// ...
}

IOException 중에 FileSystemException, RemoteExceptoin 을 처리한다.

주어진 예외 인스턴스를 원래 형식으로 다시 throw 하여 처리하지 않도록 선택할 수도 있다. 특정 context의 일치에만 관심있는 때에 유용하다.

@ExceptionHandler@Controller 클래스에서만 적용된다. 

그러나 @ControllerAdvice가 적용되면 @ControllerAdvice 내부에 있는 모든 ExceptionHandler는 모든 컨트롤러에 적용될 수 있다.